from __future__ import with_statement

import os
from xml.parsers.expat import ExpatError

from orpg.external.etree.ElementTree import ElementTree, Element
from  orpg.orpgCore import open_rpg
from orpg.dirpath import dir_struct
from orpg.tools.validate import validate
from orpg.tools.decorators import pending_deprecation, debugging

class Settings(object):
    changes = []
    filename = dir_struct["user"] + "settings.xml"
    _settings = ElementTree()
    _default = ElementTree()

    def get(self, setting, default=None):
        try:
            return self._settings.iter_find(setting).get('value', default)
        except AttributeError:
            return default

    def set(self, setting, value):
        try:
            self._settings.iter_find(setting).set('value', str(value))
        except AttributeError:
            raise AttributeError("%s does not exist, try adding it first" % setting)

    def add(self, tab, setting, value, options="", help_txt=""):
        if self.get(setting, None) is not None:
            return False

        new = Element(setting)
        new.set('value', value)
        new.set('options', options)
        new.set('help', help_txt)

        if isinstance(tab, str):
            tab = self._find_tab(tab)

        try:
            tab.append(new)
            return True
        except AttributeError:
            return False

    def new_tab(self, name, display_type, parent=None):
        if parent:
            if isinstance(parent, str):
                parent = self._find_tab(parent)
        else:
            parent = self._settings.getroot()

        if self._find_tab(name, parent) is not None:
            return False

        new = Element('tab')
        new.set('name', name)
        new.set('type', display_type)

        try:
            parent.append(new)
            return True
        except AttributeError:
            return False

    def get_setting_keys(self):
        """
        Used by /settings command
        """
        keys = []
        for tab in self._settings.getiterator('tab'):
            if tab.get('type') == 'grid':
                for setting in tab.getchildren():
                    keys.append(setting.tag)

        return keys

    def save(self):
        with open(self.filename, "w") as f:
            self._settings.write(f)

    """
    The following are depreciated methods, and will be removed in a future
    Release
    """
    @pending_deprecation("This method is no longer public")
    def rebuildSettings(self):
        self._rebuild()

    @pending_deprecation("use settings.get(name[, default]) instead")
    def get_setting(self, name):
        return self.get(name, None)

    @pending_deprecation("use settings.set(name, value) instead")
    def set_setting(self, name, value):
        self.set(name, value)

    @pending_deprecation("use settings.add(tab, setting[, options[, "
                         "help_txt]]) instead")
    def add_setting(self, tab, setting, value, options, help):
        return self.add(tab, setting, value, options, help)

    @pending_deprecation("use settings.new_tab(name, display_type[, parent]) "
                         "instead")
    def add_tab(self, parent, tabname, tabtype):
        return self.new_tab(tabname, tabtype, parent)

    @pending_deprecation("Thyis method is no longer public")
    def updateIni(self):
        self._merge_settings()

    """
    Private methods
    """
    def __new__(cls):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it._init()
        return it

    def _init(self):
        validate.config_file("settings.xml", "default_settings.xml")
        try:
            with open(self.filename) as f:
                self._settings.parse(f)
        except ExpatError:
            self._rebuild()
        else:
            self._merge_settings()

    def _find_tab(self, name, parent=None):
        if parent:
            if isinstance(parent, str):
                parent = self._find_tab(parent)
        else:
            parent = self._settings.getroot()

        for tab in parent.getiterator('tab'):
            if tab.get('name') == name:
                return tab

    def _merge_settings(self):
        self._default.parse(dir_struct['template'] + 'default_settings.xml')

        def merge(root):
            for child in root.getchildren():
                if child.tag == 'tab':
                    self.new_tab(child.get('name'), child.get('type'),
                                 root.get('name'))
                    merge(child)
                else:
                    self.add(root.get('name'), child.tag, child.get('value'),
                             child.get('options'), child.get('help'))

        merge(self._default.getroot())
        self.save()

    def _rebuild(self):
        try:
            os.remove(self.filename)
        except IOError:
            pass

        self._init()
        self.save()


settings = Settings()